﻿using Core.Entities;
using Core.Tools;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;

namespace Core.ObjectOperate.EmitImpl
{
    public abstract class ObjectOperater : IObjectOperater
    {
        private static readonly AssemblyBuilder AssBuilder;
        private static readonly ModuleBuilder ModBuilder;

        private static readonly int ObjectTypeHashCode = typeof(object).GetHashCode();
        private static readonly FieldInfo MethodsParamsTypesField = typeof(ObjectOperater).GetField("MethodsParamsTypes", BindingFlags.NonPublic | BindingFlags.Instance);
        private static readonly FieldInfo IndexesParamsTypesField = typeof(ObjectOperater).GetField("IndexesParamsTypes", BindingFlags.NonPublic | BindingFlags.Instance);
        private static readonly FieldInfo ConstructorsParamsTypesField = typeof(ObjectOperater).GetField("ConstructorsParamsTypes", BindingFlags.NonPublic | BindingFlags.Instance);

        private static readonly Dictionary<Type, ObjectOperater> ObjectOperaterCache = new Dictionary<Type, ObjectOperater>();
        private static readonly object ObjectOperaterCacheLock = new object();
        private static int ObjectOperaterId = 1;


        static ObjectOperater()
        {
            AssemblyName AssName = new AssemblyName(
                "ObjectOperater"
                );

            AssBuilder =
                AppDomain.CurrentDomain.DefineDynamicAssembly(
                    AssName,
                    AssemblyBuilderAccess.Run);

            ModBuilder = AssBuilder.DefineDynamicModule(
                "ObjectOperater");
        }

        protected static int ComputeMethodSignHash(MethodInfo MethodInfo)
        {
            return MethodInfo.Name.GetHashCode() ^ (MethodInfo.GetParameters().Length * ObjectTypeHashCode);
        }

        protected static int ComputeMethodSignHash(string MethodName, object[] Params)
        {
            return MethodName.GetHashCode() ^ (Params.Length * ObjectTypeHashCode);
        }

        protected static bool MethodSignEquals(string InputMethodName, object[] InputParams, string MethodName, Type[] Params)
        {
            if (MethodName == InputMethodName && Params.Length == InputParams.Length)
            {
                for (int i = 0; i < Params.Length; i++)
                {
                    if (!(Params[i].IsInstanceOfType(InputParams[i])))
                    {
                        return false;
                    }
                }

                return true;
            }

            return false;
        }

        protected static int ComputeParamsSignHash(PropertyInfo IndexInfo)
        {
            return (IndexInfo.GetIndexParameters().Length * ObjectTypeHashCode);
        }

        protected static int ComputeParamsSignHash(ConstructorInfo Constructor)
        {
            return (Constructor.GetParameters().Length * ObjectTypeHashCode);
        }

        protected static int ComputeParamsSignHash(object[] Params)
        {
            return (Params.Length * ObjectTypeHashCode);
        }

        protected static bool ParamsSignEquals(object[] InputParams, Type[] Params)
        {
            if (Params.Length == InputParams.Length)
            {
                for (int i = 0; i < Params.Length; i++)
                {
                    if (!(Params[i].IsInstanceOfType(InputParams[i])))
                    {
                        return false;
                    }
                }

                return true;
            }

            return false;
        }

        private static long GetMethodCompareValue(MethodInfo Method)
        {
            return 0;
        }

        private static void EachMethods(Type Type, Action<MethodInfo> Function)
        {
            if (Type == null)
            {
                return;
            }

            EachMethods(Type.BaseType, Function);

            foreach (var Item in Type.GetMethods(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance))
            {
                Function(Item);
            }
        }

        private static Type[] GetMethodParamsType(MethodBase Method)
        {
            var Params = Method.GetParameters();

            var Result = new Type[Params.Length];

            for (int i = 0; i < Params.Length; i++)
            {
                Result[i] = Params[i].ParameterType;
            }

            return Result;
        }

        private static Type[] GetMethodParamsType(PropertyInfo Index)
        {
            var Params = Index.GetIndexParameters();

            var Result = new Type[Params.Length];

            for (int i = 0; i < Params.Length; i++)
            {
                Result[i] = Params[i].ParameterType;
            }

            return Result;
        }

        private static MethodInfo[] GetMethodsIsNotGenericAndRemovedDuplicateSignaturesAndProperties(Type Type, PropertyInfo[] Properties)
        {
            var MethodsMap = new Dictionary<MethodSign, MethodInfo>();

            EachMethods(Type, (Item) =>
            {
                if (Item.IsGenericMethod)
                {
                    return;
                }

                var MethodSign = new MethodSign(Item.Name, GetMethodParamsType(Item), typeof(void));

                MethodsMap[MethodSign] = Item;
            });

            var MethodsMap2 = new Dictionary<MethodInfo, bool>();

            foreach (var Item in MethodsMap)
            {
                MethodsMap2.Add(Item.Value, true);
            }

            foreach (var Item in Properties)
            {
                var GetMethod = Item.GetGetMethod();
                var SetMethod = Item.GetSetMethod();

                if (GetMethod != null)
                {
                    MethodsMap2.Remove(GetMethod);
                }

                if (SetMethod != null)
                {
                    MethodsMap2.Remove(SetMethod);
                }
            }

            var Result = new MethodInfo[MethodsMap2.Count];

            MethodsMap2.Keys.CopyTo(Result, 0);

            return Result;
        }


        public static ObjectOperater Create(Type Type)
        {
            if (Type == null)
            {
                throw new NullReferenceException("Type");
            }

            ObjectOperater Result;

            if (!ObjectOperaterCache.TryGetValue(Type, out Result))
            {
                lock (ObjectOperaterCacheLock)
                {
                    if (!ObjectOperaterCache.TryGetValue(Type, out Result))
                    {
                        Result = InternalCreate(Type);

                        ObjectOperaterCache.Add(Type, Result);
                    }
                }
            }

            return Result;
        }

        private static ObjectOperater InternalCreate(Type Type)
        {
            var ObjectOperaterName = Type.Name + "_" + ObjectOperaterId;

            ++ObjectOperaterId;

            var ObjectOperaterBuilder = ModBuilder.DefineType(
                ObjectOperaterName,
                TypeAttributes.AutoLayout | TypeAttributes.AnsiClass | TypeAttributes.Sealed | TypeAttributes.Public,
                typeof(ObjectOperater));

            ObjectOperaterBuilder.AddInterfaceImplementation(typeof(IObjectOperater));

            var PropertiesWithIndexes = Type.GetProperties(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance);

            var Fields = Type.GetFields(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance);
            var Properties = ArrayHelper.Filter(PropertiesWithIndexes, (Item) => { return Item.GetIndexParameters().Length == 0; }, (Item) => { return Item; });
            var Indexes = ArrayHelper.Filter(PropertiesWithIndexes, (Item) => { return Item.GetIndexParameters().Length != 0; }, (Item) => { return Item; });
            var Methods = GetMethodsIsNotGenericAndRemovedDuplicateSignaturesAndProperties(Type, PropertiesWithIndexes);
            var Constructors = Type.GetConstructors(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance);



            ImplConstructor(ObjectOperaterBuilder, Type, Methods, Indexes, Constructors);

            ImplType(ObjectOperaterBuilder, Type);

            ImplGetFieldValue(ObjectOperaterBuilder, Fields);
            ImplSetFieldValue(ObjectOperaterBuilder, Fields);
            ImplGetFieldValueByIndex(ObjectOperaterBuilder, Fields);
            ImplSetFieldValueByIndex(ObjectOperaterBuilder, Fields);
            ImplFieldsCount(ObjectOperaterBuilder, Fields);
            ImplGetFieldIndex(ObjectOperaterBuilder, Fields);
            ImplGetFieldType(ObjectOperaterBuilder, Fields);
            ImplGetFieldName(ObjectOperaterBuilder, Fields);
            ImplGetFieldTypeByIndex(ObjectOperaterBuilder, Fields);

            ImplGetPropertyValue(ObjectOperaterBuilder, Properties);
            ImplSetPropertyValue(ObjectOperaterBuilder, Properties);
            ImplGetPropertyValueByIndex(ObjectOperaterBuilder, Properties);
            ImplSetPropertyValueByIndex(ObjectOperaterBuilder, Properties);
            ImplPropertiesCount(ObjectOperaterBuilder, Properties);
            ImplGetPropertyIndex(ObjectOperaterBuilder, Properties);
            ImplGetPropertyType(ObjectOperaterBuilder, Properties);
            ImplGetPropertyName(ObjectOperaterBuilder, Properties);
            ImplGetPropertyTypeByIndex(ObjectOperaterBuilder, Properties);


            ImplGetIndexValue(ObjectOperaterBuilder, Indexes);
            ImplSetIndexValue(ObjectOperaterBuilder, Indexes);
            ImplGetIndexValueByIndex(ObjectOperaterBuilder, Indexes);
            ImplSetIndexValueByIndex(ObjectOperaterBuilder, Indexes);
            ImplIndexesCount(ObjectOperaterBuilder, Indexes);

            ImplInvokeMethod(ObjectOperaterBuilder, Methods);
            ImplInvokeMethodByIndex(ObjectOperaterBuilder, Methods);
            ImplMethodsCount(ObjectOperaterBuilder, Methods);


            ImplNew(ObjectOperaterBuilder, Type.GetConstructor(Type.EmptyTypes));
            ImplNew(ObjectOperaterBuilder, Constructors);
            ImplConstructorsCount(ObjectOperaterBuilder, Constructors);

            return (ObjectOperater)ObjectOperaterBuilder.CreateType().GetConstructor(Type.EmptyTypes).Invoke(null);
        }



        public static void ImplConstructor(TypeBuilder ObjectOperaterBuilder, Type Type, MethodInfo[] Methods, PropertyInfo[] Indexes, ConstructorInfo[] Constructors)
        {
            var ConstructorBuilder = ObjectOperaterBuilder.DefineConstructor(MethodAttributes.Public | MethodAttributes.HideBySig,
                CallingConventions.HasThis,
                new Type[] { });

            var ConstructorILGenerator = ConstructorBuilder.GetILGenerator();

            var GetTypeFromHandleMathod = typeof(Type).GetMethod("GetTypeFromHandle");

            ConstructorILGenerator.Emit_Ldarg(0);
            ConstructorILGenerator.Emit(OpCodes.Ldtoken, Type);
            ConstructorILGenerator.Emit(OpCodes.Call, GetTypeFromHandleMathod);
            ConstructorILGenerator.Emit(OpCodes.Ldc_I8, (long)Tools.Pointer.UnBox(Methods));
            ConstructorILGenerator.Emit(OpCodes.Conv_I);
            ConstructorILGenerator.Emit(OpCodes.Ldc_I8, (long)Tools.Pointer.UnBox(Indexes));
            ConstructorILGenerator.Emit(OpCodes.Conv_I);
            ConstructorILGenerator.Emit(OpCodes.Ldc_I8, (long)Tools.Pointer.UnBox(Constructors));
            ConstructorILGenerator.Emit(OpCodes.Conv_I);
            ConstructorILGenerator.Emit(OpCodes.Call, typeof(ObjectOperater).GetConstructor(new Type[] { typeof(Type), typeof(MethodInfo[]), typeof(PropertyInfo[]), typeof(ConstructorInfo[]) }));
            ConstructorILGenerator.Emit(OpCodes.Ret);
        }

        public static void ImplType(TypeBuilder ObjectOperaterBuilder, Type Type)
        {
            var GetPropertiesCountMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "get_Type",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                typeof(Type),
                new Type[] { });

            var GetPropertiesCountILGenerator = GetPropertiesCountMethodBuilder.GetILGenerator();

            var GetTypeFromHandleMathod = typeof(Type).GetMethod("GetTypeFromHandle");

            GetPropertiesCountILGenerator.Emit(OpCodes.Ldtoken, Type);
            GetPropertiesCountILGenerator.Emit(OpCodes.Call, GetTypeFromHandleMathod);
            GetPropertiesCountILGenerator.Emit(OpCodes.Ret);
        }

        public static void ImplGetFieldValue(TypeBuilder ObjectOperaterBuilder, FieldInfo[] Fields)
        {
            var GetFieldValueMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "GetFieldValue",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConventions.HasThis,
                typeof(object),
                new Type[] { typeof(object), typeof(string) });

            var GetFieldValueILGenerator = GetFieldValueMethodBuilder.GetILGenerator();

            if (Fields.Length != 0)
            {
                var Cases = new CaseInfo<string>[Fields.Length];

                var DefaultLabel = GetFieldValueILGenerator.DefineLabel();

                for (int i = 0; i < Fields.Length; i++)
                {
                    var FieldName = Fields[i].Name;

                    Cases[i] = new CaseInfo<string>(FieldName, GetFieldValueILGenerator.DefineLabel());
                }

                GetFieldValueILGenerator.Emit_Switch((ILGen) => { ILGen.Emit_Ldarg(2); },
                    Cases,
                    DefaultLabel);

                GetFieldValueILGenerator.MarkLabel(DefaultLabel);
                GetFieldValueILGenerator.Emit(OpCodes.Ldstr, "Name");
                GetFieldValueILGenerator.Emit(OpCodes.Newobj, typeof(KeyNotFoundException).GetConstructor(new Type[] { typeof(string) }));
                GetFieldValueILGenerator.Emit(OpCodes.Throw);

                for (int i = 0; i < Fields.Length; i++)
                {
                    var FieldInfo = Fields[i];
                    var CaseInfo = Cases[i];

                    GetFieldValueILGenerator.MarkLabel(CaseInfo.Label);
                    GetFieldValueILGenerator.Emit_Ldarg(1);
                    GetFieldValueILGenerator.Emit(OpCodes.Ldfld, FieldInfo);

                    if (FieldInfo.FieldType.IsValueType)
                    {
                        GetFieldValueILGenerator.Emit(OpCodes.Box, FieldInfo.FieldType);
                    }

                    GetFieldValueILGenerator.Emit(OpCodes.Ret);
                }
            }
            else
            {
                GetFieldValueILGenerator.Emit(OpCodes.Ldstr, "Name");
                GetFieldValueILGenerator.Emit(OpCodes.Newobj, typeof(KeyNotFoundException).GetConstructor(new Type[] { typeof(string) }));
                GetFieldValueILGenerator.Emit(OpCodes.Throw);
            }
        }

        public static void ImplSetFieldValue(TypeBuilder ObjectOperaterBuilder, FieldInfo[] Fields)
        {
            var GetFieldValueMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "SetFieldValue",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConventions.HasThis,
                typeof(void),
                new Type[] { typeof(object), typeof(string), typeof(object) });

            var SetFieldValueILGenerator = GetFieldValueMethodBuilder.GetILGenerator();

            if (Fields.Length != 0)
            {
                var Cases = new CaseInfo<string>[Fields.Length];

                var DefaultLabel = SetFieldValueILGenerator.DefineLabel();

                for (int i = 0; i < Fields.Length; i++)
                {
                    var FieldName = Fields[i].Name;

                    Cases[i] = new CaseInfo<string>(FieldName, SetFieldValueILGenerator.DefineLabel());
                }

                SetFieldValueILGenerator.Emit_Switch((ILGen) => { ILGen.Emit_Ldarg(2); },
                    Cases,
                    DefaultLabel);

                SetFieldValueILGenerator.MarkLabel(DefaultLabel);
                SetFieldValueILGenerator.Emit(OpCodes.Ldstr, "Name");
                SetFieldValueILGenerator.Emit(OpCodes.Newobj, typeof(KeyNotFoundException).GetConstructor(new Type[] { typeof(string) }));
                SetFieldValueILGenerator.Emit(OpCodes.Throw);

                for (int i = 0; i < Fields.Length; i++)
                {
                    var FieldInfo = Fields[i];
                    var CaseInfo = Cases[i];

                    SetFieldValueILGenerator.MarkLabel(CaseInfo.Label);
                    SetFieldValueILGenerator.Emit_Ldarg(1);
                    SetFieldValueILGenerator.Emit_Ldarg(3);

                    if (FieldInfo.FieldType.IsValueType)
                    {
                        SetFieldValueILGenerator.Emit(OpCodes.Unbox_Any, FieldInfo.FieldType);
                    }

                    SetFieldValueILGenerator.Emit(OpCodes.Stfld, FieldInfo);



                    SetFieldValueILGenerator.Emit(OpCodes.Ret);
                }
            }
            else
            {
                SetFieldValueILGenerator.Emit(OpCodes.Ldstr, "Name");
                SetFieldValueILGenerator.Emit(OpCodes.Newobj, typeof(KeyNotFoundException).GetConstructor(new Type[] { typeof(string) }));
                SetFieldValueILGenerator.Emit(OpCodes.Throw);
            }
        }

        public static void ImplGetFieldValueByIndex(TypeBuilder ObjectOperaterBuilder, FieldInfo[] Fields)
        {
            var GetFieldValueMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "GetFieldValue",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConventions.HasThis,
                typeof(object),
                new Type[] { typeof(object), typeof(int) });

            var GetFieldValueILGenerator = GetFieldValueMethodBuilder.GetILGenerator();

            if (Fields.Length != 0)
            {
                var Cases = new CaseInfo<int>[Fields.Length];

                var DefaultLabel = GetFieldValueILGenerator.DefineLabel();

                for (int i = 0; i < Fields.Length; i++)
                {
                    var FieldName = Fields[i].Name;

                    Cases[i] = new CaseInfo<int>(i, GetFieldValueILGenerator.DefineLabel());
                }

                GetFieldValueILGenerator.Emit_Switch((ILGen) => { ILGen.Emit_Ldarg(2); }, Cases, DefaultLabel);

                GetFieldValueILGenerator.MarkLabel(DefaultLabel);
                GetFieldValueILGenerator.Emit(OpCodes.Ldstr, "Index");
                GetFieldValueILGenerator.Emit(OpCodes.Newobj, typeof(IndexOutOfRangeException).GetConstructor(new Type[] { typeof(string) }));
                GetFieldValueILGenerator.Emit(OpCodes.Throw);

                for (int i = 0; i < Fields.Length; i++)
                {
                    var FieldInfo = Fields[i];
                    var CaseInfo = Cases[i];

                    GetFieldValueILGenerator.MarkLabel(CaseInfo.Label);
                    GetFieldValueILGenerator.Emit_Ldarg(1);
                    GetFieldValueILGenerator.Emit(OpCodes.Ldfld, FieldInfo);

                    if (FieldInfo.FieldType.IsValueType)
                    {
                        GetFieldValueILGenerator.Emit(OpCodes.Box, FieldInfo.FieldType);
                    }

                    GetFieldValueILGenerator.Emit(OpCodes.Ret);
                }
            }
            else
            {
                GetFieldValueILGenerator.Emit(OpCodes.Ldstr, "Index");
                GetFieldValueILGenerator.Emit(OpCodes.Newobj, typeof(IndexOutOfRangeException).GetConstructor(new Type[] { typeof(string) }));
                GetFieldValueILGenerator.Emit(OpCodes.Throw);
            }
        }

        public static void ImplSetFieldValueByIndex(TypeBuilder ObjectOperaterBuilder, FieldInfo[] Fields)
        {
            var GetFieldValueMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "SetFieldValue",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConventions.HasThis,
                typeof(void),
                new Type[] { typeof(object), typeof(int), typeof(object) });

            var SetFieldValueILGenerator = GetFieldValueMethodBuilder.GetILGenerator();

            if (Fields.Length != 0)
            {
                var Cases = new CaseInfo<int>[Fields.Length];

                var DefaultLabel = SetFieldValueILGenerator.DefineLabel();

                for (int i = 0; i < Fields.Length; i++)
                {
                    var FieldName = Fields[i].Name;

                    Cases[i] = new CaseInfo<int>(i, SetFieldValueILGenerator.DefineLabel());
                }

                SetFieldValueILGenerator.Emit_Switch((ILGen) => { ILGen.Emit_Ldarg(2); }, Cases, DefaultLabel);

                SetFieldValueILGenerator.MarkLabel(DefaultLabel);
                SetFieldValueILGenerator.Emit(OpCodes.Ldstr, "Index");
                SetFieldValueILGenerator.Emit(OpCodes.Newobj, typeof(IndexOutOfRangeException).GetConstructor(new Type[] { typeof(string) }));
                SetFieldValueILGenerator.Emit(OpCodes.Throw);

                for (int i = 0; i < Fields.Length; i++)
                {
                    var FieldInfo = Fields[i];
                    var CaseInfo = Cases[i];

                    SetFieldValueILGenerator.MarkLabel(CaseInfo.Label);
                    SetFieldValueILGenerator.Emit_Ldarg(1);
                    SetFieldValueILGenerator.Emit_Ldarg(3);

                    if (FieldInfo.FieldType.IsValueType)
                    {
                        SetFieldValueILGenerator.Emit(OpCodes.Unbox_Any, FieldInfo.FieldType);
                    }

                    SetFieldValueILGenerator.Emit(OpCodes.Stfld, FieldInfo);



                    SetFieldValueILGenerator.Emit(OpCodes.Ret);
                }
            }
            else
            {
                SetFieldValueILGenerator.Emit(OpCodes.Ldstr, "Index");
                SetFieldValueILGenerator.Emit(OpCodes.Newobj, typeof(IndexOutOfRangeException).GetConstructor(new Type[] { typeof(string) }));
                SetFieldValueILGenerator.Emit(OpCodes.Throw);
            }
        }

        public static void ImplGetFieldIndex(TypeBuilder ObjectOperaterBuilder, FieldInfo[] Fields)
        {
            var GetFieldValueMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "GetFieldIndex",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConventions.HasThis,
                typeof(int),
                new Type[] { typeof(string) });

            var GetFieldValueILGenerator = GetFieldValueMethodBuilder.GetILGenerator();

            if (Fields.Length != 0)
            {
                var Cases = new CaseInfo<string>[Fields.Length];

                var DefaultLabel = GetFieldValueILGenerator.DefineLabel();

                for (int i = 0; i < Fields.Length; i++)
                {
                    var FieldName = Fields[i].Name;

                    Cases[i] = new CaseInfo<string>(FieldName, GetFieldValueILGenerator.DefineLabel());
                }

                GetFieldValueILGenerator.Emit_Switch((ILGen) => { ILGen.Emit_Ldarg(1); },
                    Cases,
                    DefaultLabel);

                GetFieldValueILGenerator.MarkLabel(DefaultLabel);
                GetFieldValueILGenerator.Emit(OpCodes.Ldstr, "Name");
                GetFieldValueILGenerator.Emit(OpCodes.Newobj, typeof(KeyNotFoundException).GetConstructor(new Type[] { typeof(string) }));
                GetFieldValueILGenerator.Emit(OpCodes.Throw);

                for (int i = 0; i < Fields.Length; i++)
                {
                    var FieldInfo = Fields[i];
                    var CaseInfo = Cases[i];

                    GetFieldValueILGenerator.MarkLabel(CaseInfo.Label);
                    GetFieldValueILGenerator.Emit_Ldc_I4(i);
                    GetFieldValueILGenerator.Emit(OpCodes.Ret);
                }
            }
            else
            {
                GetFieldValueILGenerator.Emit(OpCodes.Ldstr, "Name");
                GetFieldValueILGenerator.Emit(OpCodes.Newobj, typeof(KeyNotFoundException).GetConstructor(new Type[] { typeof(string) }));
                GetFieldValueILGenerator.Emit(OpCodes.Throw);
            }
        }

        public static void ImplGetFieldType(TypeBuilder ObjectOperaterBuilder, FieldInfo[] Fields)
        {
            var GetFieldValueMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "GetFieldType",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConventions.HasThis,
                typeof(Type),
                new Type[] { typeof(string) });

            var GetFieldValueILGenerator = GetFieldValueMethodBuilder.GetILGenerator();

            if (Fields.Length != 0)
            {
                var Cases = new CaseInfo<string>[Fields.Length];

                var DefaultLabel = GetFieldValueILGenerator.DefineLabel();

                for (int i = 0; i < Fields.Length; i++)
                {
                    var FieldName = Fields[i].Name;

                    Cases[i] = new CaseInfo<string>(FieldName, GetFieldValueILGenerator.DefineLabel());
                }

                GetFieldValueILGenerator.Emit_Switch((ILGen) => { ILGen.Emit_Ldarg(1); },
                    Cases,
                    DefaultLabel);

                GetFieldValueILGenerator.MarkLabel(DefaultLabel);
                GetFieldValueILGenerator.Emit(OpCodes.Ldstr, "Name");
                GetFieldValueILGenerator.Emit(OpCodes.Newobj, typeof(KeyNotFoundException).GetConstructor(new Type[] { typeof(string) }));
                GetFieldValueILGenerator.Emit(OpCodes.Throw);

                var GetTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle");

                for (int i = 0; i < Fields.Length; i++)
                {
                    var FieldInfo = Fields[i];
                    var CaseInfo = Cases[i];

                    GetFieldValueILGenerator.MarkLabel(CaseInfo.Label);
                    GetFieldValueILGenerator.Emit(OpCodes.Ldtoken, FieldInfo.FieldType);
                    GetFieldValueILGenerator.Emit(OpCodes.Call, GetTypeFromHandle);
                    GetFieldValueILGenerator.Emit(OpCodes.Ret);

                }
            }
            else
            {
                GetFieldValueILGenerator.Emit(OpCodes.Ldstr, "Name");
                GetFieldValueILGenerator.Emit(OpCodes.Newobj, typeof(KeyNotFoundException).GetConstructor(new Type[] { typeof(string) }));
                GetFieldValueILGenerator.Emit(OpCodes.Throw);
            }
        }

        public static void ImplGetFieldName(TypeBuilder ObjectOperaterBuilder, FieldInfo[] Fields)
        {
            var GetFieldValueMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "GetFieldName",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConventions.HasThis,
                typeof(string),
                new Type[] { typeof(int) });

            var GetFieldValueILGenerator = GetFieldValueMethodBuilder.GetILGenerator();

            if (Fields.Length != 0)
            {
                var Cases = new CaseInfo<int>[Fields.Length];

                var DefaultLabel = GetFieldValueILGenerator.DefineLabel();

                for (int i = 0; i < Fields.Length; i++)
                {
                    var FieldName = Fields[i].Name;

                    Cases[i] = new CaseInfo<int>(i, GetFieldValueILGenerator.DefineLabel());
                }

                GetFieldValueILGenerator.Emit_Switch((ILGen) => { ILGen.Emit_Ldarg(1); }, Cases, DefaultLabel);

                GetFieldValueILGenerator.MarkLabel(DefaultLabel);
                GetFieldValueILGenerator.Emit(OpCodes.Ldstr, "Index");
                GetFieldValueILGenerator.Emit(OpCodes.Newobj, typeof(IndexOutOfRangeException).GetConstructor(new Type[] { typeof(string) }));
                GetFieldValueILGenerator.Emit(OpCodes.Throw);

                for (int i = 0; i < Fields.Length; i++)
                {
                    var FieldInfo = Fields[i];
                    var CaseInfo = Cases[i];

                    GetFieldValueILGenerator.MarkLabel(CaseInfo.Label);
                    GetFieldValueILGenerator.Emit(OpCodes.Ldstr, FieldInfo.Name);
                    GetFieldValueILGenerator.Emit(OpCodes.Ret);
                }
            }
            else
            {
                GetFieldValueILGenerator.Emit(OpCodes.Ldstr, "Index");
                GetFieldValueILGenerator.Emit(OpCodes.Newobj, typeof(IndexOutOfRangeException).GetConstructor(new Type[] { typeof(string) }));
                GetFieldValueILGenerator.Emit(OpCodes.Throw);
            }
        }

        public static void ImplGetFieldTypeByIndex(TypeBuilder ObjectOperaterBuilder, FieldInfo[] Fields)
        {
            var GetFieldValueMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "GetFieldType",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConventions.HasThis,
                typeof(Type),
                new Type[] { typeof(int) });

            var GetFieldValueILGenerator = GetFieldValueMethodBuilder.GetILGenerator();

            if (Fields.Length != 0)
            {
                var Cases = new CaseInfo<int>[Fields.Length];

                var DefaultLabel = GetFieldValueILGenerator.DefineLabel();

                for (int i = 0; i < Fields.Length; i++)
                {
                    var FieldName = Fields[i].Name;

                    Cases[i] = new CaseInfo<int>(i, GetFieldValueILGenerator.DefineLabel());
                }

                GetFieldValueILGenerator.Emit_Switch((ILGen) => { ILGen.Emit_Ldarg(1); }, Cases, DefaultLabel);

                GetFieldValueILGenerator.MarkLabel(DefaultLabel);
                GetFieldValueILGenerator.Emit(OpCodes.Ldstr, "Index");
                GetFieldValueILGenerator.Emit(OpCodes.Newobj, typeof(IndexOutOfRangeException).GetConstructor(new Type[] { typeof(string) }));
                GetFieldValueILGenerator.Emit(OpCodes.Throw);

                var GetTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle");

                for (int i = 0; i < Fields.Length; i++)
                {
                    var FieldInfo = Fields[i];
                    var CaseInfo = Cases[i];

                    GetFieldValueILGenerator.MarkLabel(CaseInfo.Label);
                    GetFieldValueILGenerator.Emit(OpCodes.Ldtoken, FieldInfo.FieldType);
                    GetFieldValueILGenerator.Emit(OpCodes.Call, GetTypeFromHandle);
                    GetFieldValueILGenerator.Emit(OpCodes.Ret);
                }
            }
            else
            {
                GetFieldValueILGenerator.Emit(OpCodes.Ldstr, "Index");
                GetFieldValueILGenerator.Emit(OpCodes.Newobj, typeof(IndexOutOfRangeException).GetConstructor(new Type[] { typeof(string) }));
                GetFieldValueILGenerator.Emit(OpCodes.Throw);
            }
        }

        public static void ImplFieldsCount(TypeBuilder ObjectOperaterBuilder, FieldInfo[] Fields)
        {
            var GetFieldsCountMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "get_FieldsCount",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConventions.HasThis,
                typeof(int),
                new Type[] { });

            var GetFieldsCountILGenerator = GetFieldsCountMethodBuilder.GetILGenerator();

            GetFieldsCountILGenerator.Emit_Ldc_I4(Fields.Length);
            GetFieldsCountILGenerator.Emit(OpCodes.Ret);
        }

        public static void ImplGetPropertyValue(TypeBuilder ObjectOperaterBuilder, PropertyInfo[] Properties)
        {
            var GetPropertyValueMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "GetPropertyValue",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.Final,
                CallingConventions.HasThis,
                typeof(object),
                new Type[] { typeof(object), typeof(string) });

            var GetPropertyValueILGenerator = GetPropertyValueMethodBuilder.GetILGenerator();

            if (Properties.Length != 0)
            {
                var Cases = new CaseInfo<string>[Properties.Length];

                var DefaultLabel = GetPropertyValueILGenerator.DefineLabel();

                for (int i = 0; i < Properties.Length; i++)
                {
                    var FieldName = Properties[i].Name;

                    Cases[i] = new CaseInfo<string>(FieldName, GetPropertyValueILGenerator.DefineLabel());
                }

                GetPropertyValueILGenerator.Emit_Switch((ILGen) => { ILGen.Emit_Ldarg(2); },
                    Cases,
                    DefaultLabel);

                GetPropertyValueILGenerator.MarkLabel(DefaultLabel);
                GetPropertyValueILGenerator.Emit(OpCodes.Ldstr, "Name");
                GetPropertyValueILGenerator.Emit(OpCodes.Newobj, typeof(KeyNotFoundException).GetConstructor(new Type[] { typeof(string) }));
                GetPropertyValueILGenerator.Emit(OpCodes.Throw);

                for (int i = 0; i < Properties.Length; i++)
                {
                    var PropertyInfo = Properties[i];
                    var CaseInfo = Cases[i];

                    GetPropertyValueILGenerator.MarkLabel(CaseInfo.Label);
                    var GetMethod = PropertyInfo.GetGetMethod();

                    if (PropertyInfo.CanRead && GetMethod != null)
                    {
                        GetPropertyValueILGenerator.Emit_Ldarg(1);
                        GetPropertyValueILGenerator.Emit(OpCodes.Call, GetMethod);

                        if (PropertyInfo.PropertyType.IsValueType)
                        {
                            GetPropertyValueILGenerator.Emit(OpCodes.Box, PropertyInfo.PropertyType);
                        }

                        GetPropertyValueILGenerator.Emit(OpCodes.Ret);
                    }
                    else
                    {
                        GetPropertyValueILGenerator.Emit(OpCodes.Ldstr, "This property no get method or cannot access -- " + PropertyInfo.Name);
                        GetPropertyValueILGenerator.Emit(OpCodes.Newobj, typeof(Exception).GetConstructor(new Type[] { typeof(string) }));
                        GetPropertyValueILGenerator.Emit(OpCodes.Throw);
                    }

                }
            }
            else
            {
                GetPropertyValueILGenerator.Emit(OpCodes.Ldstr, "Name");
                GetPropertyValueILGenerator.Emit(OpCodes.Newobj, typeof(KeyNotFoundException).GetConstructor(new Type[] { typeof(string) }));
                GetPropertyValueILGenerator.Emit(OpCodes.Throw);
            }
        }

        public static void ImplSetPropertyValue(TypeBuilder ObjectOperaterBuilder, PropertyInfo[] Properties)
        {
            var SetPropertyValueMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "SetPropertyValue",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConventions.HasThis,
                typeof(void),
                new Type[] { typeof(object), typeof(string), typeof(object) });

            var SetPropertyValueILGenerator = SetPropertyValueMethodBuilder.GetILGenerator();

            if (Properties.Length != 0)
            {
                var Cases = new CaseInfo<string>[Properties.Length];

                var DefaultLabel = SetPropertyValueILGenerator.DefineLabel();

                for (int i = 0; i < Properties.Length; i++)
                {
                    var FieldName = Properties[i].Name;

                    Cases[i] = new CaseInfo<string>(FieldName, SetPropertyValueILGenerator.DefineLabel());
                }

                SetPropertyValueILGenerator.Emit_Switch((ILGen) => { ILGen.Emit_Ldarg(2); },
                    Cases,
                    DefaultLabel);

                SetPropertyValueILGenerator.MarkLabel(DefaultLabel);
                SetPropertyValueILGenerator.Emit(OpCodes.Ldstr, "Name");
                SetPropertyValueILGenerator.Emit(OpCodes.Newobj, typeof(KeyNotFoundException).GetConstructor(new Type[] { typeof(string) }));
                SetPropertyValueILGenerator.Emit(OpCodes.Throw);

                for (int i = 0; i < Properties.Length; i++)
                {
                    var PropertyInfo = Properties[i];
                    var CaseInfo = Cases[i];

                    SetPropertyValueILGenerator.MarkLabel(CaseInfo.Label);

                    var SetMethod = PropertyInfo.GetSetMethod();

                    if (PropertyInfo.CanRead && SetMethod != null)
                    {
                        SetPropertyValueILGenerator.Emit_Ldarg(1);
                        SetPropertyValueILGenerator.Emit_Ldarg(3);

                        if (PropertyInfo.PropertyType.IsValueType)
                        {
                            SetPropertyValueILGenerator.Emit(OpCodes.Unbox_Any, PropertyInfo.PropertyType);
                        }

                        SetPropertyValueILGenerator.Emit(OpCodes.Call, SetMethod);

                        SetPropertyValueILGenerator.Emit(OpCodes.Ret);
                    }
                    else
                    {
                        SetPropertyValueILGenerator.Emit(OpCodes.Ldstr, "This property no set method or cannot access -- " + PropertyInfo.Name);
                        SetPropertyValueILGenerator.Emit(OpCodes.Newobj, typeof(Exception).GetConstructor(new Type[] { typeof(string) }));
                        SetPropertyValueILGenerator.Emit(OpCodes.Throw);
                    }
                }
            }
            else
            {
                SetPropertyValueILGenerator.Emit(OpCodes.Ldstr, "Name");
                SetPropertyValueILGenerator.Emit(OpCodes.Newobj, typeof(KeyNotFoundException).GetConstructor(new Type[] { typeof(string) }));
                SetPropertyValueILGenerator.Emit(OpCodes.Throw);
            }
        }

        public static void ImplGetPropertyValueByIndex(TypeBuilder ObjectOperaterBuilder, PropertyInfo[] Properties)
        {
            var GetPropertyValueMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "GetPropertyValue",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConventions.HasThis,
                typeof(object),
                new Type[] { typeof(object), typeof(int) });

            var GetPropertyValueILGenerator = GetPropertyValueMethodBuilder.GetILGenerator();

            if (Properties.Length != 0)
            {
                var Cases = new CaseInfo<int>[Properties.Length];

                var DefaultLabel = GetPropertyValueILGenerator.DefineLabel();

                for (int i = 0; i < Properties.Length; i++)
                {
                    var FieldName = Properties[i].Name;

                    Cases[i] = new CaseInfo<int>(i, GetPropertyValueILGenerator.DefineLabel());
                }

                GetPropertyValueILGenerator.Emit_Switch((ILGen) => { ILGen.Emit_Ldarg(2); }, Cases, DefaultLabel);

                GetPropertyValueILGenerator.MarkLabel(DefaultLabel);
                GetPropertyValueILGenerator.Emit(OpCodes.Ldstr, "Index");
                GetPropertyValueILGenerator.Emit(OpCodes.Newobj, typeof(IndexOutOfRangeException).GetConstructor(new Type[] { typeof(string) }));
                GetPropertyValueILGenerator.Emit(OpCodes.Throw);

                for (int i = 0; i < Properties.Length; i++)
                {
                    var PropertyInfo = Properties[i];
                    var CaseInfo = Cases[i];

                    GetPropertyValueILGenerator.MarkLabel(CaseInfo.Label);
                    var GetMethod = PropertyInfo.GetGetMethod();

                    if (PropertyInfo.CanRead && GetMethod != null)
                    {
                        GetPropertyValueILGenerator.Emit_Ldarg(1);
                        GetPropertyValueILGenerator.Emit(OpCodes.Call, GetMethod);

                        if (PropertyInfo.PropertyType.IsValueType)
                        {
                            GetPropertyValueILGenerator.Emit(OpCodes.Box, PropertyInfo.PropertyType);
                        }

                        GetPropertyValueILGenerator.Emit(OpCodes.Ret);
                    }
                    else
                    {
                        GetPropertyValueILGenerator.Emit(OpCodes.Ldstr, "This property no get method or cannot access -- " + PropertyInfo.Name);
                        GetPropertyValueILGenerator.Emit(OpCodes.Newobj, typeof(Exception).GetConstructor(new Type[] { typeof(string) }));
                        GetPropertyValueILGenerator.Emit(OpCodes.Throw);
                    }

                }
            }
            else
            {
                GetPropertyValueILGenerator.Emit(OpCodes.Ldstr, "Index");
                GetPropertyValueILGenerator.Emit(OpCodes.Newobj, typeof(IndexOutOfRangeException).GetConstructor(new Type[] { typeof(string) }));
                GetPropertyValueILGenerator.Emit(OpCodes.Throw);
            }
        }

        public static void ImplSetPropertyValueByIndex(TypeBuilder ObjectOperaterBuilder, PropertyInfo[] Properties)
        {
            var SetPropertyValueMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "SetPropertyValue",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConventions.HasThis,
                typeof(void),
                new Type[] { typeof(object), typeof(int), typeof(object) });

            var SetPropertyValueILGenerator = SetPropertyValueMethodBuilder.GetILGenerator();

            if (Properties.Length != 0)
            {
                var Cases = new CaseInfo<int>[Properties.Length];

                var DefaultLabel = SetPropertyValueILGenerator.DefineLabel();

                for (int i = 0; i < Properties.Length; i++)
                {
                    var FieldName = Properties[i].Name;

                    Cases[i] = new CaseInfo<int>(i, SetPropertyValueILGenerator.DefineLabel());
                }

                SetPropertyValueILGenerator.Emit_Switch((ILGen) => { ILGen.Emit_Ldarg(2); }, Cases, DefaultLabel);

                SetPropertyValueILGenerator.MarkLabel(DefaultLabel);
                SetPropertyValueILGenerator.Emit(OpCodes.Ldstr, "Index");
                SetPropertyValueILGenerator.Emit(OpCodes.Newobj, typeof(IndexOutOfRangeException).GetConstructor(new Type[] { typeof(string) }));
                SetPropertyValueILGenerator.Emit(OpCodes.Throw);

                for (int i = 0; i < Properties.Length; i++)
                {
                    var PropertyInfo = Properties[i];
                    var CaseInfo = Cases[i];

                    SetPropertyValueILGenerator.MarkLabel(CaseInfo.Label);

                    var SetMethod = PropertyInfo.GetSetMethod();

                    if (PropertyInfo.CanRead && SetMethod != null)
                    {
                        SetPropertyValueILGenerator.Emit_Ldarg(1);
                        SetPropertyValueILGenerator.Emit_Ldarg(3);

                        if (PropertyInfo.PropertyType.IsValueType)
                        {
                            SetPropertyValueILGenerator.Emit(OpCodes.Unbox_Any, PropertyInfo.PropertyType);
                        }

                        SetPropertyValueILGenerator.Emit(OpCodes.Call, SetMethod);

                        SetPropertyValueILGenerator.Emit(OpCodes.Ret);
                    }
                    else
                    {
                        SetPropertyValueILGenerator.Emit(OpCodes.Ldstr, "This property no set method or cannot access -- " + PropertyInfo.Name);
                        SetPropertyValueILGenerator.Emit(OpCodes.Newobj, typeof(Exception).GetConstructor(new Type[] { typeof(string) }));
                        SetPropertyValueILGenerator.Emit(OpCodes.Throw);
                    }
                }
            }
            else
            {
                SetPropertyValueILGenerator.Emit(OpCodes.Ldstr, "Index");
                SetPropertyValueILGenerator.Emit(OpCodes.Newobj, typeof(IndexOutOfRangeException).GetConstructor(new Type[] { typeof(string) }));
                SetPropertyValueILGenerator.Emit(OpCodes.Throw);
            }
        }

        public static void ImplGetPropertyIndex(TypeBuilder ObjectOperaterBuilder, PropertyInfo[] Properties)
        {
            var GetPropertyValueMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "GetPropertyIndex",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConventions.HasThis,
                typeof(int),
                new Type[] { typeof(string) });

            var GetPropertyValueILGenerator = GetPropertyValueMethodBuilder.GetILGenerator();

            if (Properties.Length != 0)
            {
                var Cases = new CaseInfo<string>[Properties.Length];

                var DefaultLabel = GetPropertyValueILGenerator.DefineLabel();

                for (int i = 0; i < Properties.Length; i++)
                {
                    var FieldName = Properties[i].Name;

                    Cases[i] = new CaseInfo<string>(FieldName, GetPropertyValueILGenerator.DefineLabel());
                }

                GetPropertyValueILGenerator.Emit_Switch((ILGen) => { ILGen.Emit_Ldarg(1); },
                    Cases,
                    DefaultLabel);

                GetPropertyValueILGenerator.MarkLabel(DefaultLabel);
                GetPropertyValueILGenerator.Emit(OpCodes.Ldstr, "Name");
                GetPropertyValueILGenerator.Emit(OpCodes.Newobj, typeof(KeyNotFoundException).GetConstructor(new Type[] { typeof(string) }));
                GetPropertyValueILGenerator.Emit(OpCodes.Throw);

                for (int i = 0; i < Properties.Length; i++)
                {
                    var PropertyInfo = Properties[i];
                    var CaseInfo = Cases[i];

                    GetPropertyValueILGenerator.MarkLabel(CaseInfo.Label);
                    GetPropertyValueILGenerator.Emit_Ldc_I4(i);
                    GetPropertyValueILGenerator.Emit(OpCodes.Ret);
                }
            }
            else
            {
                GetPropertyValueILGenerator.Emit(OpCodes.Ldstr, "Name");
                GetPropertyValueILGenerator.Emit(OpCodes.Newobj, typeof(KeyNotFoundException).GetConstructor(new Type[] { typeof(string) }));
                GetPropertyValueILGenerator.Emit(OpCodes.Throw);
            }
        }

        public static void ImplGetPropertyType(TypeBuilder ObjectOperaterBuilder, PropertyInfo[] Properties)
        {
            var GetPropertyValueMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "GetPropertyType",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConventions.HasThis,
                typeof(Type),
                new Type[] { typeof(string) });

            var GetPropertyValueILGenerator = GetPropertyValueMethodBuilder.GetILGenerator();

            if (Properties.Length != 0)
            {
                var Cases = new CaseInfo<string>[Properties.Length];

                var DefaultLabel = GetPropertyValueILGenerator.DefineLabel();

                for (int i = 0; i < Properties.Length; i++)
                {
                    var FieldName = Properties[i].Name;

                    Cases[i] = new CaseInfo<string>(FieldName, GetPropertyValueILGenerator.DefineLabel());
                }

                GetPropertyValueILGenerator.Emit_Switch((ILGen) => { ILGen.Emit_Ldarg(1); },
                    Cases,
                    DefaultLabel);

                GetPropertyValueILGenerator.MarkLabel(DefaultLabel);
                GetPropertyValueILGenerator.Emit(OpCodes.Ldstr, "Name");
                GetPropertyValueILGenerator.Emit(OpCodes.Newobj, typeof(KeyNotFoundException).GetConstructor(new Type[] { typeof(string) }));
                GetPropertyValueILGenerator.Emit(OpCodes.Throw);

                var GetTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle");

                for (int i = 0; i < Properties.Length; i++)
                {
                    var PropertyInfo = Properties[i];
                    var CaseInfo = Cases[i];

                    GetPropertyValueILGenerator.MarkLabel(CaseInfo.Label);
                    GetPropertyValueILGenerator.Emit(OpCodes.Ldtoken, PropertyInfo.PropertyType);
                    GetPropertyValueILGenerator.Emit(OpCodes.Call, GetTypeFromHandle);
                    GetPropertyValueILGenerator.Emit(OpCodes.Ret);

                }
            }
            else
            {
                GetPropertyValueILGenerator.Emit(OpCodes.Ldstr, "Name");
                GetPropertyValueILGenerator.Emit(OpCodes.Newobj, typeof(KeyNotFoundException).GetConstructor(new Type[] { typeof(string) }));
                GetPropertyValueILGenerator.Emit(OpCodes.Throw);
            }
        }

        public static void ImplGetPropertyName(TypeBuilder ObjectOperaterBuilder, PropertyInfo[] Properties)
        {
            var GetPropertyValueMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "GetPropertyName",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConventions.HasThis,
                typeof(string),
                new Type[] { typeof(int) });

            var GetPropertyValueILGenerator = GetPropertyValueMethodBuilder.GetILGenerator();

            if (Properties.Length != 0)
            {
                var Cases = new CaseInfo<int>[Properties.Length];

                var DefaultLabel = GetPropertyValueILGenerator.DefineLabel();

                for (int i = 0; i < Properties.Length; i++)
                {
                    var FieldName = Properties[i].Name;

                    Cases[i] = new CaseInfo<int>(i, GetPropertyValueILGenerator.DefineLabel());
                }

                GetPropertyValueILGenerator.Emit_Switch((ILGen) => { ILGen.Emit_Ldarg(1); }, Cases, DefaultLabel);

                GetPropertyValueILGenerator.MarkLabel(DefaultLabel);
                GetPropertyValueILGenerator.Emit(OpCodes.Ldstr, "Index");
                GetPropertyValueILGenerator.Emit(OpCodes.Newobj, typeof(IndexOutOfRangeException).GetConstructor(new Type[] { typeof(string) }));
                GetPropertyValueILGenerator.Emit(OpCodes.Throw);

                for (int i = 0; i < Properties.Length; i++)
                {
                    var PropertyInfo = Properties[i];
                    var CaseInfo = Cases[i];

                    GetPropertyValueILGenerator.MarkLabel(CaseInfo.Label);
                    GetPropertyValueILGenerator.Emit(OpCodes.Ldstr, PropertyInfo.Name);
                    GetPropertyValueILGenerator.Emit(OpCodes.Ret);
                }
            }
            else
            {
                GetPropertyValueILGenerator.Emit(OpCodes.Ldstr, "Index");
                GetPropertyValueILGenerator.Emit(OpCodes.Newobj, typeof(IndexOutOfRangeException).GetConstructor(new Type[] { typeof(string) }));
                GetPropertyValueILGenerator.Emit(OpCodes.Throw);
            }
        }

        public static void ImplGetPropertyTypeByIndex(TypeBuilder ObjectOperaterBuilder, PropertyInfo[] Properties)
        {
            var GetPropertyValueMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "GetPropertyType",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConventions.HasThis,
                typeof(Type),
                new Type[] { typeof(int) });

            var GetPropertyValueILGenerator = GetPropertyValueMethodBuilder.GetILGenerator();

            if (Properties.Length != 0)
            {
                var Cases = new CaseInfo<int>[Properties.Length];

                var DefaultLabel = GetPropertyValueILGenerator.DefineLabel();

                for (int i = 0; i < Properties.Length; i++)
                {
                    var FieldName = Properties[i].Name;

                    Cases[i] = new CaseInfo<int>(i, GetPropertyValueILGenerator.DefineLabel());
                }

                GetPropertyValueILGenerator.Emit_Switch((ILGen) => { ILGen.Emit_Ldarg(1); }, Cases, DefaultLabel);

                GetPropertyValueILGenerator.MarkLabel(DefaultLabel);
                GetPropertyValueILGenerator.Emit(OpCodes.Ldstr, "Index");
                GetPropertyValueILGenerator.Emit(OpCodes.Newobj, typeof(IndexOutOfRangeException).GetConstructor(new Type[] { typeof(string) }));
                GetPropertyValueILGenerator.Emit(OpCodes.Throw);

                var GetTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle");

                for (int i = 0; i < Properties.Length; i++)
                {
                    var PropertyInfo = Properties[i];
                    var CaseInfo = Cases[i];

                    GetPropertyValueILGenerator.MarkLabel(CaseInfo.Label);
                    GetPropertyValueILGenerator.Emit(OpCodes.Ldtoken, PropertyInfo.PropertyType);
                    GetPropertyValueILGenerator.Emit(OpCodes.Call, GetTypeFromHandle);
                    GetPropertyValueILGenerator.Emit(OpCodes.Ret);
                }
            }
            else
            {
                GetPropertyValueILGenerator.Emit(OpCodes.Ldstr, "Index");
                GetPropertyValueILGenerator.Emit(OpCodes.Newobj, typeof(IndexOutOfRangeException).GetConstructor(new Type[] { typeof(string) }));
                GetPropertyValueILGenerator.Emit(OpCodes.Throw);
            }
        }

        public static void ImplPropertiesCount(TypeBuilder ObjectOperaterBuilder, PropertyInfo[] Properties)
        {
            var GetPropertiesCountMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "get_PropertiesCount",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConventions.HasThis,
                typeof(int),
                new Type[] { });

            var GetPropertiesCountILGenerator = GetPropertiesCountMethodBuilder.GetILGenerator();

            GetPropertiesCountILGenerator.Emit_Ldc_I4(Properties.Length);
            GetPropertiesCountILGenerator.Emit(OpCodes.Ret);
        }

        public static void ImplConstructorsCount(TypeBuilder ObjectOperaterBuilder, ConstructorInfo[] Constructors)
        {
            var GetPropertiesCountMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "get_ConstructorsCount",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConventions.HasThis,
                typeof(int),
                new Type[] { });

            var GetPropertiesCountILGenerator = GetPropertiesCountMethodBuilder.GetILGenerator();

            GetPropertiesCountILGenerator.Emit_Ldc_I4(Constructors.Length);
            GetPropertiesCountILGenerator.Emit(OpCodes.Ret);
        }

        public static void ImplMethodsCount(TypeBuilder ObjectOperaterBuilder, MethodInfo[] Methods)
        {
            var GetPropertiesCountMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "get_MethodsCount",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConventions.HasThis,
                typeof(int),
                new Type[] { });

            var GetPropertiesCountILGenerator = GetPropertiesCountMethodBuilder.GetILGenerator();

            GetPropertiesCountILGenerator.Emit_Ldc_I4(Methods.Length);
            GetPropertiesCountILGenerator.Emit(OpCodes.Ret);
        }

        public static void ImplIndexesCount(TypeBuilder ObjectOperaterBuilder, PropertyInfo[] Indexes)
        {
            var GetPropertiesCountMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "get_IndexesCount",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConventions.HasThis,
                typeof(int),
                new Type[] { });

            var GetPropertiesCountILGenerator = GetPropertiesCountMethodBuilder.GetILGenerator();

            GetPropertiesCountILGenerator.Emit_Ldc_I4(Indexes.Length);
            GetPropertiesCountILGenerator.Emit(OpCodes.Ret);
        }

        public unsafe static void ImplInvokeMethodByIndex(TypeBuilder ObjectOperaterBuilder, MethodInfo[] Methods)
        {
            var InvokeMethodMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "InvokeMethod",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConventions.HasThis,
                typeof(object),
                new Type[] { typeof(object), typeof(int), typeof(object[]) });

            var InvokeMethodILGenerator = InvokeMethodMethodBuilder.GetILGenerator();

            if (Methods.Length != 0)
            {
                var Cases = new CaseInfo<int>[Methods.Length];

                var DefaultLabel = InvokeMethodILGenerator.DefineLabel();

                for (int i = 0; i < Methods.Length; i++)
                {
                    var MethodName = Methods[i].Name;

                    Cases[i] = new CaseInfo<int>(i, InvokeMethodILGenerator.DefineLabel());
                }

                InvokeMethodILGenerator.Emit_Switch((ILGen) => { ILGen.Emit_Ldarg(2); }, Cases, DefaultLabel);

                InvokeMethodILGenerator.MarkLabel(DefaultLabel);
                InvokeMethodILGenerator.Emit(OpCodes.Ldstr, "Index");
                InvokeMethodILGenerator.Emit(OpCodes.Newobj, typeof(IndexOutOfRangeException).GetConstructor(new Type[] { typeof(string) }));
                InvokeMethodILGenerator.Emit(OpCodes.Throw);

                for (int i = 0; i < Methods.Length; i++)
                {
                    var MethodInfo = Methods[i];
                    var CaseInfo = Cases[i];

                    var DeclaringType = MethodInfo.DeclaringType;
                    var Params = MethodInfo.GetParameters();
                    var ResultType = MethodInfo.ReturnType;

                    InvokeMethodILGenerator.MarkLabel(CaseInfo.Label);
                    InvokeMethodILGenerator.Emit_Ldarg(1);

                    if (DeclaringType.IsValueType)
                    {
                        InvokeMethodILGenerator.Emit_Ldc_I4(sizeof(IntPtr));
                        InvokeMethodILGenerator.Emit(OpCodes.Add);
                    }

                    for (int j = 0; j < Params.Length; j++)
                    {
                        InvokeMethodILGenerator.Emit_Ldarg(3);
                        InvokeMethodILGenerator.Emit_Ldc_I4(j);

                        var ParamType = Params[j].ParameterType;

                        if (ParamType.IsByRef)
                        {
                            ParamType = ParamType.GetElementType();

                            if (ParamType.IsValueType)
                            {
                                InvokeMethodILGenerator.Emit(OpCodes.Ldelem_Ref);
                                InvokeMethodILGenerator.Emit_Ldc_I4(sizeof(IntPtr));
                                InvokeMethodILGenerator.Emit(OpCodes.Add);
                            }
                            else
                            {
                                InvokeMethodILGenerator.Emit(OpCodes.Ldelema, typeof(object));
                            }
                        }
                        else
                        {
                            InvokeMethodILGenerator.Emit(OpCodes.Ldelem_Ref);
                            InvokeMethodILGenerator.Emit_Cast(ParamType);
                        }
                    }

                    InvokeMethodILGenerator.Emit(OpCodes.Call, MethodInfo);

                    if (ResultType == typeof(void))
                    {
                        InvokeMethodILGenerator.Emit(OpCodes.Ldnull);
                    }
                    else if (ResultType.IsValueType)
                    {
                        InvokeMethodILGenerator.Emit(OpCodes.Box, ResultType);
                    }

                    InvokeMethodILGenerator.Emit(OpCodes.Ret);
                }
            }
            else
            {
                InvokeMethodILGenerator.Emit(OpCodes.Ldstr, "Index");
                InvokeMethodILGenerator.Emit(OpCodes.Newobj, typeof(IndexOutOfRangeException).GetConstructor(new Type[] { typeof(string) }));
                InvokeMethodILGenerator.Emit(OpCodes.Throw);
            }
        }

        public unsafe static void ImplInvokeMethod(TypeBuilder ObjectOperaterBuilder, MethodInfo[] Methods)
        {
            var InvokeMethodMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "InvokeMethod",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConventions.HasThis,
                typeof(object),
                new Type[] { typeof(object), typeof(string), typeof(object[]) });

            var InvokeMethodILGenerator = InvokeMethodMethodBuilder.GetILGenerator();

            if (Methods.Length != 0)
            {
                var Cases = new CaseInfo<int>[Methods.Length];

                var DefaultLabel = InvokeMethodILGenerator.DefineLabel();

                for (int i = 0; i < Methods.Length; i++)
                {
                    Cases[i] = new CaseInfo<int>(i, ComputeMethodSignHash(Methods[i]), InvokeMethodILGenerator.DefineLabel(), (ILGen, Index) =>
                    {
                        ILGen.Emit(OpCodes.Ldstr, Methods[Index].Name);
                        ILGen.Emit(OpCodes.Ldarg, 0);
                        ILGen.Emit(OpCodes.Ldfld, MethodsParamsTypesField);
                        ILGen.Emit_Ldc_I4(Index);
                        ILGen.Emit(OpCodes.Ldelem_Ref);
                    });
                }

                InvokeMethodILGenerator.Emit_Switch(
                    (ILGen) =>
                    {
                        ILGen.Emit_Ldarg(2);
                        ILGen.Emit_Ldarg(3);
                    },
                    ((Func<string, object[], int>)ComputeMethodSignHash).Method,
                    ((Func<string, object[], string, Type[], bool>)MethodSignEquals).Method, Cases, DefaultLabel);

                InvokeMethodILGenerator.MarkLabel(DefaultLabel);
                InvokeMethodILGenerator.Emit(OpCodes.Ldstr, "Name");
                InvokeMethodILGenerator.Emit(OpCodes.Newobj, typeof(KeyNotFoundException).GetConstructor(new Type[] { typeof(string) }));
                InvokeMethodILGenerator.Emit(OpCodes.Throw);

                for (int i = 0; i < Methods.Length; i++)
                {
                    var MethodInfo = Methods[i];
                    var CaseInfo = Cases[i];

                    var DeclaringType = MethodInfo.DeclaringType;
                    var Params = MethodInfo.GetParameters();
                    var ResultType = MethodInfo.ReturnType;

                    InvokeMethodILGenerator.MarkLabel(CaseInfo.Label);
                    InvokeMethodILGenerator.Emit_Ldarg(1);

                    if (DeclaringType.IsValueType)
                    {
                        InvokeMethodILGenerator.Emit_Ldc_I4(sizeof(IntPtr));
                        InvokeMethodILGenerator.Emit(OpCodes.Add);
                    }

                    for (int j = 0; j < Params.Length; j++)
                    {
                        InvokeMethodILGenerator.Emit_Ldarg(3);
                        InvokeMethodILGenerator.Emit_Ldc_I4(j);

                        var ParamType = Params[j].ParameterType;

                        if (ParamType.IsByRef)
                        {
                            ParamType = ParamType.GetElementType();

                            if (ParamType.IsValueType)
                            {
                                InvokeMethodILGenerator.Emit(OpCodes.Ldelem_Ref);
                                InvokeMethodILGenerator.Emit_Ldc_I4(sizeof(IntPtr));
                                InvokeMethodILGenerator.Emit(OpCodes.Add);
                            }
                            else
                            {
                                InvokeMethodILGenerator.Emit(OpCodes.Ldelema, typeof(object));
                            }
                        }
                        else
                        {
                            InvokeMethodILGenerator.Emit(OpCodes.Ldelem_Ref);
                            InvokeMethodILGenerator.Emit_Cast(ParamType);
                        }
                    }

                    InvokeMethodILGenerator.Emit(OpCodes.Call, MethodInfo);

                    if (ResultType == typeof(void))
                    {
                        InvokeMethodILGenerator.Emit(OpCodes.Ldnull);
                    }
                    else if (ResultType.IsValueType)
                    {
                        InvokeMethodILGenerator.Emit(OpCodes.Box, ResultType);
                    }

                    InvokeMethodILGenerator.Emit(OpCodes.Ret);
                }
            }
            else
            {
                InvokeMethodILGenerator.Emit(OpCodes.Ldstr, "Name");
                InvokeMethodILGenerator.Emit(OpCodes.Newobj, typeof(KeyNotFoundException).GetConstructor(new Type[] { typeof(string) }));
                InvokeMethodILGenerator.Emit(OpCodes.Throw);
            }
        }

        public unsafe static void ImplSetIndexValue(TypeBuilder ObjectOperaterBuilder, PropertyInfo[] Indexes)
        {
            var InvokeMethodMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "SetIndexValue",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConventions.HasThis,
                typeof(void),
                new Type[] { typeof(object), typeof(object[]), typeof(object) });

            var InvokeMethodILGenerator = InvokeMethodMethodBuilder.GetILGenerator();

            if (Indexes.Length != 0)
            {
                var Cases = new CaseInfo<int>[Indexes.Length];

                var DefaultLabel = InvokeMethodILGenerator.DefineLabel();

                for (int i = 0; i < Indexes.Length; i++)
                {
                    Cases[i] = new CaseInfo<int>(i, ComputeParamsSignHash(Indexes[i]), InvokeMethodILGenerator.DefineLabel(), (ILGen, Index) =>
                    {
                        ILGen.Emit(OpCodes.Ldarg, 0);
                        ILGen.Emit(OpCodes.Ldfld, IndexesParamsTypesField);
                        ILGen.Emit_Ldc_I4(Index);
                        ILGen.Emit(OpCodes.Ldelem_Ref);
                    });
                }

                InvokeMethodILGenerator.Emit_Switch(
                    (ILGen) =>
                    {
                        ILGen.Emit_Ldarg(2);
                    },
                    ((Func<object[], int>)ComputeParamsSignHash).Method,
                    ((Func<object[], Type[], bool>)ParamsSignEquals).Method, Cases, DefaultLabel);

                InvokeMethodILGenerator.MarkLabel(DefaultLabel);
                InvokeMethodILGenerator.Emit(OpCodes.Ldstr, "Index");
                InvokeMethodILGenerator.Emit(OpCodes.Newobj, typeof(KeyNotFoundException).GetConstructor(new Type[] { typeof(string) }));
                InvokeMethodILGenerator.Emit(OpCodes.Throw);

                for (int i = 0; i < Indexes.Length; i++)
                {
                    var CaseInfo = Cases[i];

                    InvokeMethodILGenerator.MarkLabel(CaseInfo.Label);

                    var MethodInfo = Indexes[i].GetSetMethod();

                    if (MethodInfo == null)
                    {
                        InvokeMethodILGenerator.Emit(OpCodes.Ldstr, "This indexer no set method or cannot access.");
                        InvokeMethodILGenerator.Emit(OpCodes.Newobj, typeof(MethodAccessException).GetConstructor(new Type[] { typeof(string) }));
                        InvokeMethodILGenerator.Emit(OpCodes.Throw);

                        continue;
                    }

                    var DeclaringType = MethodInfo.DeclaringType;
                    var Params = MethodInfo.GetParameters();

                    InvokeMethodILGenerator.Emit_Ldarg(1);

                    if (DeclaringType.IsValueType)
                    {
                        InvokeMethodILGenerator.Emit_Ldc_I4(sizeof(IntPtr));
                        InvokeMethodILGenerator.Emit(OpCodes.Add);
                    }

                    Type ParamType;

                    int Right = Params.Length - 1;

                    for (int j = 0; j < Right; j++)
                    {
                        InvokeMethodILGenerator.Emit_Ldarg(2);
                        InvokeMethodILGenerator.Emit_Ldc_I4(j);

                        ParamType = Params[j].ParameterType;

                        if (ParamType.IsByRef)
                        {
                            ParamType = ParamType.GetElementType();

                            if (ParamType.IsValueType)
                            {
                                InvokeMethodILGenerator.Emit(OpCodes.Ldelem_Ref);
                                InvokeMethodILGenerator.Emit_Ldc_I4(sizeof(IntPtr));
                                InvokeMethodILGenerator.Emit(OpCodes.Add);
                            }
                            else
                            {
                                InvokeMethodILGenerator.Emit(OpCodes.Ldelema, typeof(object));
                            }
                        }
                        else
                        {
                            InvokeMethodILGenerator.Emit(OpCodes.Ldelem_Ref);
                            InvokeMethodILGenerator.Emit_Cast(ParamType);
                        }
                    }

                    ParamType = Params[Right].ParameterType;

                    if (ParamType.IsByRef)
                    {
                        ParamType = ParamType.GetElementType();

                        if (ParamType.IsValueType)
                        {
                            InvokeMethodILGenerator.Emit_Ldarg(3);
                            InvokeMethodILGenerator.Emit_Ldc_I4(sizeof(IntPtr));
                            InvokeMethodILGenerator.Emit(OpCodes.Add);
                        }
                        else
                        {
                            InvokeMethodILGenerator.Emit_Ldarga(3);
                        }
                    }
                    else
                    {
                        InvokeMethodILGenerator.Emit_Ldarg(3);

                        if (ParamType.IsValueType)
                        {
                            InvokeMethodILGenerator.Emit(OpCodes.Unbox_Any, ParamType);
                        }
                    }

                    InvokeMethodILGenerator.Emit(OpCodes.Call, MethodInfo);

                    InvokeMethodILGenerator.Emit(OpCodes.Ret);
                }
            }
            else
            {
                InvokeMethodILGenerator.Emit(OpCodes.Ldstr, "Index");
                InvokeMethodILGenerator.Emit(OpCodes.Newobj, typeof(KeyNotFoundException).GetConstructor(new Type[] { typeof(string) }));
                InvokeMethodILGenerator.Emit(OpCodes.Throw);
            }
        }

        public unsafe static void ImplGetIndexValue(TypeBuilder ObjectOperaterBuilder, PropertyInfo[] Indexes)
        {
            var InvokeMethodMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "GetIndexValue",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConventions.HasThis,
                typeof(object),
                new Type[] { typeof(object), typeof(object[]) });

            var InvokeMethodILGenerator = InvokeMethodMethodBuilder.GetILGenerator();

            if (Indexes.Length != 0)
            {
                var Cases = new CaseInfo<int>[Indexes.Length];

                var DefaultLabel = InvokeMethodILGenerator.DefineLabel();

                for (int i = 0; i < Indexes.Length; i++)
                {
                    Cases[i] = new CaseInfo<int>(i, ComputeParamsSignHash(Indexes[i]), InvokeMethodILGenerator.DefineLabel(), (ILGen, Index) =>
                    {
                        ILGen.Emit(OpCodes.Ldarg, 0);
                        ILGen.Emit(OpCodes.Ldfld, IndexesParamsTypesField);
                        ILGen.Emit_Ldc_I4(Index);
                        ILGen.Emit(OpCodes.Ldelem_Ref);
                    });
                }

                InvokeMethodILGenerator.Emit_Switch(
                    (ILGen) =>
                    {
                        ILGen.Emit_Ldarg(2);
                    },
                    ((Func<object[], int>)ComputeParamsSignHash).Method,
                    ((Func<object[], Type[], bool>)ParamsSignEquals).Method, Cases, DefaultLabel);

                InvokeMethodILGenerator.MarkLabel(DefaultLabel);
                InvokeMethodILGenerator.Emit(OpCodes.Ldstr, "Index");
                InvokeMethodILGenerator.Emit(OpCodes.Newobj, typeof(KeyNotFoundException).GetConstructor(new Type[] { typeof(string) }));
                InvokeMethodILGenerator.Emit(OpCodes.Throw);

                for (int i = 0; i < Indexes.Length; i++)
                {
                    var CaseInfo = Cases[i];

                    InvokeMethodILGenerator.MarkLabel(CaseInfo.Label);

                    var MethodInfo = Indexes[i].GetGetMethod();

                    if (MethodInfo == null)
                    {
                        InvokeMethodILGenerator.Emit(OpCodes.Ldstr, "This indexer no get method or cannot access.");
                        InvokeMethodILGenerator.Emit(OpCodes.Newobj, typeof(MethodAccessException).GetConstructor(new Type[] { typeof(string) }));
                        InvokeMethodILGenerator.Emit(OpCodes.Throw);

                        continue;
                    }

                    var DeclaringType = MethodInfo.DeclaringType;
                    var Params = MethodInfo.GetParameters();
                    var ResultType = MethodInfo.ReturnType;

                    InvokeMethodILGenerator.Emit_Ldarg(1);

                    if (DeclaringType.IsValueType)
                    {
                        InvokeMethodILGenerator.Emit_Ldc_I4(sizeof(IntPtr));
                        InvokeMethodILGenerator.Emit(OpCodes.Add);
                    }

                    Type ParamType;

                    for (int j = 0; j < Params.Length; j++)
                    {
                        InvokeMethodILGenerator.Emit_Ldarg(2);
                        InvokeMethodILGenerator.Emit_Ldc_I4(j);

                        ParamType = Params[j].ParameterType;

                        if (ParamType.IsByRef)
                        {
                            ParamType = ParamType.GetElementType();

                            if (ParamType.IsValueType)
                            {
                                InvokeMethodILGenerator.Emit(OpCodes.Ldelem_Ref);
                                InvokeMethodILGenerator.Emit_Ldc_I4(sizeof(IntPtr));
                                InvokeMethodILGenerator.Emit(OpCodes.Add);
                            }
                            else
                            {
                                InvokeMethodILGenerator.Emit(OpCodes.Ldelema, typeof(object));
                            }
                        }
                        else
                        {
                            InvokeMethodILGenerator.Emit(OpCodes.Ldelem_Ref);
                            InvokeMethodILGenerator.Emit_Cast(ParamType);
                        }
                    }

                    InvokeMethodILGenerator.Emit(OpCodes.Call, MethodInfo);

                    if (ResultType.IsValueType)
                    {
                        InvokeMethodILGenerator.Emit(OpCodes.Box, ResultType);
                    }

                    InvokeMethodILGenerator.Emit(OpCodes.Ret);
                }
            }
            else
            {
                InvokeMethodILGenerator.Emit(OpCodes.Ldstr, "Index");
                InvokeMethodILGenerator.Emit(OpCodes.Newobj, typeof(KeyNotFoundException).GetConstructor(new Type[] { typeof(string) }));
                InvokeMethodILGenerator.Emit(OpCodes.Throw);
            }
        }

        public unsafe static void ImplSetIndexValueByIndex(TypeBuilder ObjectOperaterBuilder, PropertyInfo[] Indexes)
        {
            var InvokeMethodMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "SetIndexValue",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConventions.HasThis,
                typeof(void),
                new Type[] { typeof(object), typeof(int), typeof(object[]), typeof(object) });

            var InvokeMethodILGenerator = InvokeMethodMethodBuilder.GetILGenerator();

            if (Indexes.Length != 0)
            {
                var Cases = new CaseInfo<int>[Indexes.Length];

                var DefaultLabel = InvokeMethodILGenerator.DefineLabel();

                for (int i = 0; i < Indexes.Length; i++)
                {
                    Cases[i] = new CaseInfo<int>(i, InvokeMethodILGenerator.DefineLabel());
                }

                InvokeMethodILGenerator.Emit_Switch(
                    (ILGen) =>
                    {
                        ILGen.Emit_Ldarg(2);
                    },
                    Cases,
                    DefaultLabel);

                InvokeMethodILGenerator.MarkLabel(DefaultLabel);
                InvokeMethodILGenerator.Emit(OpCodes.Ldstr, "Index");
                InvokeMethodILGenerator.Emit(OpCodes.Newobj, typeof(KeyNotFoundException).GetConstructor(new Type[] { typeof(string) }));
                InvokeMethodILGenerator.Emit(OpCodes.Throw);

                for (int i = 0; i < Indexes.Length; i++)
                {
                    var CaseInfo = Cases[i];

                    InvokeMethodILGenerator.MarkLabel(CaseInfo.Label);

                    var MethodInfo = Indexes[i].GetSetMethod();

                    if (MethodInfo == null)
                    {
                        InvokeMethodILGenerator.Emit(OpCodes.Ldstr, "This indexer no set method or cannot access.");
                        InvokeMethodILGenerator.Emit(OpCodes.Newobj, typeof(MethodAccessException).GetConstructor(new Type[] { typeof(string) }));
                        InvokeMethodILGenerator.Emit(OpCodes.Throw);

                        continue;
                    }

                    var DeclaringType = MethodInfo.DeclaringType;
                    var Params = MethodInfo.GetParameters();

                    InvokeMethodILGenerator.Emit_Ldarg(1);

                    if (DeclaringType.IsValueType)
                    {
                        InvokeMethodILGenerator.Emit_Ldc_I4(sizeof(IntPtr));
                        InvokeMethodILGenerator.Emit(OpCodes.Add);
                    }

                    Type ParamType;

                    int Right = Params.Length - 1;

                    for (int j = 0; j < Right; j++)
                    {
                        InvokeMethodILGenerator.Emit_Ldarg(3);
                        InvokeMethodILGenerator.Emit_Ldc_I4(j);

                        ParamType = Params[j].ParameterType;

                        if (ParamType.IsByRef)
                        {
                            ParamType = ParamType.GetElementType();

                            if (ParamType.IsValueType)
                            {
                                InvokeMethodILGenerator.Emit(OpCodes.Ldelem_Ref);
                                InvokeMethodILGenerator.Emit_Ldc_I4(sizeof(IntPtr));
                                InvokeMethodILGenerator.Emit(OpCodes.Add);
                            }
                            else
                            {
                                InvokeMethodILGenerator.Emit(OpCodes.Ldelema, typeof(object));
                            }
                        }
                        else
                        {
                            InvokeMethodILGenerator.Emit(OpCodes.Ldelem_Ref);
                            InvokeMethodILGenerator.Emit_Cast(ParamType);
                        }
                    }

                    ParamType = Params[Right].ParameterType;

                    if (ParamType.IsByRef)
                    {
                        ParamType = ParamType.GetElementType();

                        if (ParamType.IsValueType)
                        {
                            InvokeMethodILGenerator.Emit_Ldarg(4);
                            InvokeMethodILGenerator.Emit_Ldc_I4(sizeof(IntPtr));
                            InvokeMethodILGenerator.Emit(OpCodes.Add);
                        }
                        else
                        {
                            InvokeMethodILGenerator.Emit_Ldarga(4);
                        }
                    }
                    else
                    {
                        InvokeMethodILGenerator.Emit_Ldarg(4);

                        if (ParamType.IsValueType)
                        {
                            InvokeMethodILGenerator.Emit(OpCodes.Unbox_Any, ParamType);
                        }
                    }

                    InvokeMethodILGenerator.Emit(OpCodes.Call, MethodInfo);

                    InvokeMethodILGenerator.Emit(OpCodes.Ret);
                }
            }
            else
            {
                InvokeMethodILGenerator.Emit(OpCodes.Ldstr, "Index");
                InvokeMethodILGenerator.Emit(OpCodes.Newobj, typeof(KeyNotFoundException).GetConstructor(new Type[] { typeof(string) }));
                InvokeMethodILGenerator.Emit(OpCodes.Throw);
            }
        }

        public unsafe static void ImplGetIndexValueByIndex(TypeBuilder ObjectOperaterBuilder, PropertyInfo[] Indexes)
        {
            var InvokeMethodMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "GetIndexValue",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConventions.HasThis,
                typeof(object),
                new Type[] { typeof(object), typeof(int), typeof(object[]) });

            var InvokeMethodILGenerator = InvokeMethodMethodBuilder.GetILGenerator();

            if (Indexes.Length != 0)
            {
                var Cases = new CaseInfo<int>[Indexes.Length];

                var DefaultLabel = InvokeMethodILGenerator.DefineLabel();

                for (int i = 0; i < Indexes.Length; i++)
                {
                    Cases[i] = new CaseInfo<int>(i, InvokeMethodILGenerator.DefineLabel());
                }

                InvokeMethodILGenerator.Emit_Switch(
                    (ILGen) =>
                    {
                        ILGen.Emit_Ldarg(2);
                    },
                    Cases,
                    DefaultLabel);

                InvokeMethodILGenerator.MarkLabel(DefaultLabel);
                InvokeMethodILGenerator.Emit(OpCodes.Ldstr, "Index");
                InvokeMethodILGenerator.Emit(OpCodes.Newobj, typeof(KeyNotFoundException).GetConstructor(new Type[] { typeof(string) }));
                InvokeMethodILGenerator.Emit(OpCodes.Throw);

                for (int i = 0; i < Indexes.Length; i++)
                {
                    var CaseInfo = Cases[i];

                    InvokeMethodILGenerator.MarkLabel(CaseInfo.Label);

                    var MethodInfo = Indexes[i].GetGetMethod();

                    if (MethodInfo == null)
                    {
                        InvokeMethodILGenerator.Emit(OpCodes.Ldstr, "This indexer no get method or cannot access.");
                        InvokeMethodILGenerator.Emit(OpCodes.Newobj, typeof(MethodAccessException).GetConstructor(new Type[] { typeof(string) }));
                        InvokeMethodILGenerator.Emit(OpCodes.Throw);

                        continue;
                    }

                    var DeclaringType = MethodInfo.DeclaringType;
                    var Params = MethodInfo.GetParameters();
                    var ResultType = MethodInfo.ReturnType;

                    InvokeMethodILGenerator.Emit_Ldarg(1);

                    if (DeclaringType.IsValueType)
                    {
                        InvokeMethodILGenerator.Emit_Ldc_I4(sizeof(IntPtr));
                        InvokeMethodILGenerator.Emit(OpCodes.Add);
                    }

                    Type ParamType;

                    for (int j = 0; j < Params.Length; j++)
                    {
                        InvokeMethodILGenerator.Emit_Ldarg(3);
                        InvokeMethodILGenerator.Emit_Ldc_I4(j);

                        ParamType = Params[j].ParameterType;

                        if (ParamType.IsByRef)
                        {
                            ParamType = ParamType.GetElementType();

                            if (ParamType.IsValueType)
                            {
                                InvokeMethodILGenerator.Emit(OpCodes.Ldelem_Ref);
                                InvokeMethodILGenerator.Emit_Ldc_I4(sizeof(IntPtr));
                                InvokeMethodILGenerator.Emit(OpCodes.Add);
                            }
                            else
                            {
                                InvokeMethodILGenerator.Emit(OpCodes.Ldelema, typeof(object));
                            }
                        }
                        else
                        {
                            InvokeMethodILGenerator.Emit(OpCodes.Ldelem_Ref);
                            InvokeMethodILGenerator.Emit_Cast(ParamType);
                        }
                    }

                    InvokeMethodILGenerator.Emit(OpCodes.Call, MethodInfo);

                    if (ResultType.IsValueType)
                    {
                        InvokeMethodILGenerator.Emit(OpCodes.Box, ResultType);
                    }

                    InvokeMethodILGenerator.Emit(OpCodes.Ret);
                }
            }
            else
            {
                InvokeMethodILGenerator.Emit(OpCodes.Ldstr, "Index");
                InvokeMethodILGenerator.Emit(OpCodes.Newobj, typeof(KeyNotFoundException).GetConstructor(new Type[] { typeof(string) }));
                InvokeMethodILGenerator.Emit(OpCodes.Throw);
            }
        }

        public unsafe static void ImplNew(TypeBuilder ObjectOperaterBuilder, ConstructorInfo[] Constructors)
        {
            var InvokeMethodMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "New",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConventions.HasThis,
                typeof(object),
                new Type[] { typeof(object[]) });

            var InvokeMethodILGenerator = InvokeMethodMethodBuilder.GetILGenerator();

            if (Constructors.Length != 0)
            {
                LocalBuilder ResultLocal = null;

                var Cases = new CaseInfo<int>[Constructors.Length];

                var DefaultLabel = InvokeMethodILGenerator.DefineLabel();

                for (int i = 0; i < Constructors.Length; i++)
                {
                    Cases[i] = new CaseInfo<int>(i, ComputeParamsSignHash(Constructors[i]), InvokeMethodILGenerator.DefineLabel(), (ILGen, Index) =>
                    {
                        ILGen.Emit(OpCodes.Ldarg, 0);
                        ILGen.Emit(OpCodes.Ldfld, ConstructorsParamsTypesField);
                        ILGen.Emit_Ldc_I4(Index);
                        ILGen.Emit(OpCodes.Ldelem_Ref);
                    });
                }

                InvokeMethodILGenerator.Emit_Switch(
                    (ILGen) =>
                    {
                        ILGen.Emit_Ldarg(1);
                    },
                    ((Func<object[], int>)ComputeParamsSignHash).Method,
                    ((Func<object[], Type[], bool>)ParamsSignEquals).Method, Cases, DefaultLabel);

                InvokeMethodILGenerator.MarkLabel(DefaultLabel);
                InvokeMethodILGenerator.Emit(OpCodes.Ldstr, "Constructor");
                InvokeMethodILGenerator.Emit(OpCodes.Newobj, typeof(KeyNotFoundException).GetConstructor(new Type[] { typeof(string) }));
                InvokeMethodILGenerator.Emit(OpCodes.Throw);

                for (int i = 0; i < Constructors.Length; i++)
                {
                    var CaseInfo = Cases[i];

                    InvokeMethodILGenerator.MarkLabel(CaseInfo.Label);

                    var MethodInfo = Constructors[i];

                    var DeclaringType = MethodInfo.DeclaringType;
                    var Params = MethodInfo.GetParameters();

                    if (DeclaringType.IsValueType)
                    {
                        if (ResultLocal == null)
                        {
                            ResultLocal = InvokeMethodILGenerator.DeclareLocal(DeclaringType);
                        }

                        InvokeMethodILGenerator.Emit(OpCodes.Ldloca_S, ResultLocal);
                    }

                    for (int j = 0; j < Params.Length; j++)
                    {
                        InvokeMethodILGenerator.Emit_Ldarg(1);
                        InvokeMethodILGenerator.Emit_Ldc_I4(j);

                        var ParamType = Params[j].ParameterType;

                        if (ParamType.IsByRef)
                        {
                            ParamType = ParamType.GetElementType();

                            if (ParamType.IsValueType)
                            {
                                InvokeMethodILGenerator.Emit(OpCodes.Ldelem_Ref);
                                InvokeMethodILGenerator.Emit_Ldc_I4(sizeof(IntPtr));
                                InvokeMethodILGenerator.Emit(OpCodes.Add);
                            }
                            else
                            {
                                InvokeMethodILGenerator.Emit(OpCodes.Ldelema, typeof(object));
                            }
                        }
                        else
                        {
                            InvokeMethodILGenerator.Emit(OpCodes.Ldelem_Ref);
                            InvokeMethodILGenerator.Emit_Cast(ParamType);
                        }
                    }

                    if (DeclaringType.IsValueType)
                    {
                        InvokeMethodILGenerator.Emit(OpCodes.Call, MethodInfo);
                        InvokeMethodILGenerator.Emit(OpCodes.Ldloc_S, ResultLocal);
                        InvokeMethodILGenerator.Emit(OpCodes.Box, DeclaringType);
                        InvokeMethodILGenerator.Emit(OpCodes.Ret);
                    }
                    else
                    {
                        InvokeMethodILGenerator.Emit(OpCodes.Newobj, MethodInfo);
                        InvokeMethodILGenerator.Emit(OpCodes.Ret);
                    }

                }
            }
            else
            {
                InvokeMethodILGenerator.Emit(OpCodes.Ldstr, "Constructor");
                InvokeMethodILGenerator.Emit(OpCodes.Newobj, typeof(KeyNotFoundException).GetConstructor(new Type[] { typeof(string) }));
                InvokeMethodILGenerator.Emit(OpCodes.Throw);
            }
        }

        public unsafe static void ImplNew(TypeBuilder ObjectOperaterBuilder, ConstructorInfo Constructors)
        {
            var InvokeMethodMethodBuilder = ObjectOperaterBuilder.DefineMethod(
                "New",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConventions.HasThis,
                typeof(object),
                new Type[] { });

            var InvokeMethodILGenerator = InvokeMethodMethodBuilder.GetILGenerator();

            if (Constructors == null)
            {
                InvokeMethodILGenerator.Emit(OpCodes.Ldstr, "Constructor");
                InvokeMethodILGenerator.Emit(OpCodes.Newobj, typeof(MethodAccessException).GetConstructor(new Type[] { typeof(string) }));
                InvokeMethodILGenerator.Emit(OpCodes.Throw);
            }
            else
            {
                var DeclaringType = Constructors.DeclaringType;

                if (DeclaringType.IsValueType)
                {
                    var ResultLocal = InvokeMethodILGenerator.DeclareLocal(DeclaringType);

                    InvokeMethodILGenerator.Emit(OpCodes.Ldloca_S, ResultLocal);
                    InvokeMethodILGenerator.Emit(OpCodes.Call, Constructors);
                    InvokeMethodILGenerator.Emit(OpCodes.Ldloc_S, ResultLocal);
                    InvokeMethodILGenerator.Emit(OpCodes.Box, DeclaringType);
                    InvokeMethodILGenerator.Emit(OpCodes.Ret);
                }
                else
                {
                    InvokeMethodILGenerator.Emit(OpCodes.Newobj, Constructors);
                    InvokeMethodILGenerator.Emit(OpCodes.Ret);
                }
            }
        }


        /// <summary>
        /// Key : void MethodName(ParamsTypes...)
        /// Valye : Result Type
        /// </summary>
        private readonly ParamsDictionary<MethodSign, Type> MethodsAndIndexesAndConstructors;

        protected readonly Type[][] MethodsParamsTypes;
        protected readonly Type[][] IndexesParamsTypes;
        protected readonly Type[][] ConstructorsParamsTypes;

        public ObjectOperater(Type Type, MethodInfo[] Methods, PropertyInfo[] Indexes, ConstructorInfo[] Constructors)
        {
            MethodsAndIndexesAndConstructors = new ParamsDictionary<MethodSign, Type>();

            MethodsParamsTypes = new Type[Methods.Length][];
            IndexesParamsTypes = new Type[Indexes.Length][];
            ConstructorsParamsTypes = new Type[Constructors.Length][];

            int Index = 0;

            foreach (var Item in Methods)
            {
                var MethodSign = new MethodSign(
                        Item.Name,
                        GetMethodParamsType(Item),
                        typeof(void));

                MethodsAndIndexesAndConstructors.Add(MethodSign, Item.ReturnType);

                var ParamsTypes = MethodsParamsTypes[Index] = (Type[])MethodSign.ParamsTypes.Clone();

                for (int i = 0; i < ParamsTypes.Length; i++)
                {
                    if (ParamsTypes[i].IsByRef)
                    {
                        ParamsTypes[i] = ParamsTypes[i].GetElementType();
                    }
                }

                ++Index;
            }

            Index = 0;

            foreach (var Item in Indexes)
            {
                var MethodSign = new MethodSign(
                        ".item",
                        GetMethodParamsType(Item),
                        typeof(void));

                MethodsAndIndexesAndConstructors.Add(MethodSign, Item.PropertyType);

                var ParamsTypes = IndexesParamsTypes[Index] = (Type[])MethodSign.ParamsTypes.Clone();

                for (int i = 0; i < ParamsTypes.Length; i++)
                {
                    if (ParamsTypes[i].IsByRef)
                    {
                        ParamsTypes[i] = ParamsTypes[i].GetElementType();
                    }
                }

                ++Index;
            }

            Index = 0;


            foreach (var Item in Constructors)
            {
                var MethodSign = new MethodSign(
                        ".ctor",
                        GetMethodParamsType(Item),
                        typeof(void));

                MethodsAndIndexesAndConstructors.Add(MethodSign, typeof(void));

                var ParamsTypes = ConstructorsParamsTypes[Index] = (Type[])MethodSign.ParamsTypes.Clone();

                for (int i = 0; i < ParamsTypes.Length; i++)
                {
                    if (ParamsTypes[i].IsByRef)
                    {
                        ParamsTypes[i] = ParamsTypes[i].GetElementType();
                    }
                }

                ++Index;
            }
        }

        public abstract Type Type { get; }

        public abstract int MethodsCount { get; }

        public abstract int IndexesCount { get; }

        public abstract int PropertiesCount { get; }

        public abstract int FieldsCount { get; }

        public abstract int ConstructorsCount { get; }


        public abstract object InvokeMethod(object Object, string MethodName, object[] Params);

        public abstract object InvokeMethod(object Object, int MethodIndex, object[] Params);

        public int GetMethodIndex(string MethodName, Type[] ParamsTypes)
        {
            if (MethodName == null)
            {
                throw new NullReferenceException("MethodName");
            }

            if (ParamsTypes == null)
            {
                throw new NullReferenceException("MethodTypes");
            }

            return MethodsAndIndexesAndConstructors.FindFirstIndex(new MethodSign(MethodName, ParamsTypes, typeof(void)));
        }

        public string GetMethodName(int MethodIndex)
        {
            if (MethodIndex >= 0 && MethodIndex < MethodsCount)
            {
                return MethodsAndIndexesAndConstructors[MethodIndex].Key.Name;
            }

            throw new IndexOutOfRangeException("MethodIndex");
        }

        public Type[] GetMethodTypes(int MethodIndex)
        {
            if (MethodIndex >= 0 && MethodIndex < MethodsCount)
            {
                return MethodsAndIndexesAndConstructors[MethodIndex].Key.ParamsTypes;
            }

            throw new IndexOutOfRangeException("MethodIndex");
        }

        public Type GetMethodType(int MethodIndex)
        {
            if (MethodIndex >= 0 && MethodIndex < MethodsCount)
            {
                return MethodsAndIndexesAndConstructors[MethodIndex].Value;
            }

            throw new IndexOutOfRangeException("MethodIndex");
        }

        public Type GetMethodType(string MethodName, Type[] ParamsTypes)
        {
            if (MethodName == null)
            {
                throw new NullReferenceException("MethodName");
            }

            if (ParamsTypes == null)
            {
                throw new NullReferenceException("MethodTypes");
            }

            return MethodsAndIndexesAndConstructors[new MethodSign(MethodName, ParamsTypes, typeof(void))];
        }


        public abstract object GetIndexValue(object Object, object[] Params);

        public abstract void SetIndexValue(object Object, object[] Params, object Value);

        public abstract object GetIndexValue(object Object, int IndexIndex, object[] Params);

        public abstract void SetIndexValue(object Object, int IndexIndex, object[] Params, object Value);

        public int GetIndexIndex(Type[] ParamsTypes)
        {
            if (ParamsTypes == null)
            {
                throw new NullReferenceException("IndexTypes");
            }

            return MethodsAndIndexesAndConstructors.FindFirstIndex(new MethodSign(".item", ParamsTypes, typeof(void))) - MethodsCount;
        }

        public Type[] GetIndexTypes(int IndexIndex)
        {
            if (IndexIndex >= 0 && IndexIndex < IndexesCount)
            {
                IndexIndex += MethodsCount;

                return MethodsAndIndexesAndConstructors[IndexIndex].Key.ParamsTypes;
            }

            throw new IndexOutOfRangeException("IndexIndex");
        }

        public Type GetIndexType(int IndexIndex)
        {
            if (IndexIndex >= 0 && IndexIndex < IndexesCount)
            {
                IndexIndex += MethodsCount;

                return MethodsAndIndexesAndConstructors[IndexIndex].Value;
            }

            throw new IndexOutOfRangeException("IndexIndex");
        }

        public Type GetIndexType(Type[] ParamsTypes)
        {
            if (ParamsTypes == null)
            {
                throw new NullReferenceException("IndexTypes");
            }

            return MethodsAndIndexesAndConstructors[new MethodSign(".item", ParamsTypes, typeof(void))];
        }


        public abstract object GetPropertyValue(object Object, string PropertyName);

        public abstract void SetPropertyValue(object Object, string PropertyName, object Value);

        public abstract object GetPropertyValue(object Object, int PropertyIndex);

        public abstract void SetPropertyValue(object Object, int PropertyIndex, object Value);

        public abstract int GetPropertyIndex(string PropertyName);

        public abstract string GetPropertyName(int PropertyIndex);

        public abstract Type GetPropertyType(int PropertyIndex);

        public abstract Type GetPropertyType(string PropertyName);


        public abstract object GetFieldValue(object Object, string FieldName);

        public abstract void SetFieldValue(object Object, string FieldName, object Value);

        public abstract object GetFieldValue(object Object, int FieldIndex);

        public abstract void SetFieldValue(object Object, int FieldIndex, object Value);

        public abstract int GetFieldIndex(string FieldName);

        public abstract string GetFieldName(int FieldIndex);

        public abstract Type GetFieldType(int FieldIndex);

        public abstract Type GetFieldType(string FieldName);


        public abstract object New(object[] Params);

        public abstract object New();

        public int GetConstructorIndex(Type[] ParamsTypes)
        {
            if (ParamsTypes == null)
            {
                throw new NullReferenceException("IndexTypes");
            }

            return MethodsAndIndexesAndConstructors.FindFirstIndex(new MethodSign(".item", ParamsTypes, typeof(void)));
        }

        public Type[] GetConstructorTypes(int ConstructorIndex)
        {
            if (ConstructorIndex >= 0 && ConstructorIndex < ConstructorsCount)
            {
                ConstructorIndex += MethodsCount + IndexesCount;

                return MethodsAndIndexesAndConstructors[ConstructorIndex].Key.ParamsTypes;
            }

            throw new IndexOutOfRangeException("ConstructorIndex");
        }
    }
}